package zmcc.bsp.fabric.model.response;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import zmcc.bsp.fabric.utils.JsonUtils;
import org.hyperledger.fabric.sdk.ChaincodeResponse.Status;
import org.hyperledger.fabric.sdk.ProposalResponse;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;

import java.nio.charset.Charset;
import java.util.List;

public class FabricQueryResponse<T> extends FabricResponse {
	private static Logger logger = LoggerFactory.getLogger(FabricQueryResponse.class);
	public T data;
	public Integer totalSize = 0;
	public FabricQueryResponseMetadata metadata;

	public FabricQueryResponse(int status, String errorMsg, T data) {
		super(status, errorMsg);
		this.data = data;
	}

	public FabricQueryResponseMetadata getMetadata() {
		return metadata;
	}

	public void setMetadata(FabricQueryResponseMetadata metadata) {
		this.metadata = metadata;
	}

	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}

	public Integer getTotalSize() {
		return totalSize;
	}

	public void setTotalSize(Integer totalSize) {
		this.totalSize = totalSize;
	}

	@Override
	public boolean isOk(boolean all) {
		if (all) {
			return data != null && super.isOk(all);
		}
		return super.isOk(all);
	}

	public static <T> FabricQueryResponse<T> failure(String errorMsg) {
		return new FabricQueryResponse<>(FAILURE, errorMsg, null);
	}

	public static <T> FabricQueryResponse<T> success(T data) {
		return new FabricQueryResponse<T>(SUCCESS, null, data);
	}

	public static <T> FabricQueryResponse<T> create(byte[] payload, Class<T> type) {
		if (payload == null) {
			return success(null);
		}
		T payloadData = parsePayload(new String(payload, Charset.forName("utf-8")), type);
		return success(payloadData);
	}

	@SuppressWarnings("unchecked")
	private static <T> T parsePayload(String value, Class<T> type) {
		return JsonUtils.json2Object(value,type);
	}

	public static <T> FabricQueryResponse<List<T>> many(ProposalResponse res, Class<T> type) {
		Status status = res.getStatus();
		if (status != Status.SUCCESS) {
			return failure(res.getMessage());
		} else {
			if (type != null) {
				try {
					int chaincodeStatus = res.getChaincodeActionResponseStatus();
					if (chaincodeStatus != -1) {
						byte[] payload = res.getChaincodeActionResponsePayload();
						return parsePayloadMany(new String(payload, Charset.forName("utf-8")), type);
					} else {
						return failure("Chaincode executed failure.");
					}
				} catch (InvalidArgumentException e) {
					return failure("Chaincode error: " + e.getMessage());
				}
			} else {
				return success(null);
			}
		}
	}

	public static <T> FabricQueryResponse<List<T>> many(byte[] payloads, Class<T> type) {
		return parsePayloadMany(new String(payloads, Charset.forName("utf-8")), type);
	}

	private static <T> FabricQueryResponse<List<T>> parsePayloadMany(String json, Class<T> type) {
		logger.info("================== parse payload many json is :{}",json);
		return success(JsonUtils.jsoncastListType(type,json));
	}
}
